<template>
  <div>
    <!-- <h1>{{ name }}: {{ info.age }} : {{ num }}</h1>
    <button @click="onChange">改变</button> -->
  </div>
</template>

<script>
import {
  ref,
  reactive,
  readonly,
  isProxy,
  isReactive,
  isReadonly,
  shallowReactive,
  shallowReadonly,
  toRaw,
  markRaw,
  toRefs
} from 'vue';
import Test from '@/components/Test';

export default {
  name: 'App',
  components: {
    Test
  },
  setup(props, ctx) { // 入口函数，
    const state = reactive({
      name: '张三'
    });

    const count = ref(0);

    // reactive 返回对象的响应式副本
    // 响应式转换是“深层”的——它影响所有嵌套 property
    // 返回的 proxy 是不等于原始对象的。建议只使用响应式 proxy，避免依赖原始对象。

    // reactive 将解包所有深层的 refs，同时维持 ref 的响应性。

    // const obj = reactive({});
    // obj.count = count;
    // console.log(obj.count === count.value);

    // 当将 ref 分配给 reactive property 时，ref 将被自动解包。

    // --------------------------------------------------

    // readonly 
    // 接受一个对象 (响应式或纯对象) 或 ref 并返回原始对象的只读代理。
    // 只读代理是深层的：任何被访问的嵌套 property 也是只读的。

    const copyState = readonly(state),
          copyCount = readonly(count);

    // console.log(copyCount.value === count.value); // true

    // console.log(copyState.name);

    // --------------------------------------------------

    // isProxy
    // 检查对象是否是由 reactive 或 readonly 创建的 proxy

    // console.log(isProxy(state));
    // console.log(isProxy(copyState));

    const state1 = readonly({
      name: '李四'
    })

    const state2 = {
      name: '王五'
    };
    // console.log(isProxy(state2)); // false

    const state3 = new Proxy({ // 只有reactive 和 readonly创建的proxy才会返回true
      name: '赵六'
    }, {});
    // console.log(isProxy(state3)); // false

    // --------------------------------------------------

    // isReactive
    // 检查对象是否是由 reactive 创建的响应式代理
    // 如果该代理是 readonly 创建的，但包裹了由 reactive 创建的另一个代理，它也会返回 true

    // console.log(isReactive(state)); // true
    // console.log(isReactive(copyState)); // true
    // console.log(isReactive(state1)); // false
    // console.log(isReactive(state2)); // false
    // console.log(isReactive(state3)); // false

    // --------------------------------------------------

    // isReadonly
    // 检查对象是否是由 readonly 创建的只读代理。

    // console.log(isReadonly(state)); // false
    // console.log(isReadonly(copyState)); // true
    // console.log(isReadonly(state1)); // true
    // console.log(isReadonly(state2)); // false
    // console.log(isReadonly(state3)); // false

    // --------------------------------------------------

    // shallowReactive
    // 创建一个响应式代理，它跟踪其自身 property 的响应性，但不执行嵌套对象的深层响应式转换 (暴露原始值)。
    // 与 reactive 不同，任何使用 ref 的 property 都不会被代理自动解包。

    const shallowState = shallowReactive({
      num: count,
      name: '张三',
      info: {
        age: 18
      }
    })

    shallowState.num = 4;
    // console.log(isReactive(shallowState)); // true
    // console.log(isReactive(shallowState.name)); // false
    // console.log(isReactive(shallowState.info)); // false

    const onChange = () => {
      // shallowState.num = 2;
      // shallowState.name = '李四';
      shallowState.info.age = 23;
      // 注意！！！ 改变单个深层的数据时，是非响应式的，页面不会渲染
      // 同时改变两个及以上时，页面会同步渲染
      // console.log(shallowState.info.age);
    }

    // shallowReadonly
    // 创建一个 proxy，使其自身的 property 为只读，但不执行嵌套对象的深度只读转换 (暴露原始值)。
    // 与 readonly 不同，任何使用 ref 的 property 都不会被代理自动解包。

    const shallowReadonlyState = shallowReadonly({
      name: count,
      name: '张三',
      info: {
        age: 18
      }
    });

    // console.log(isReadonly(shallowReadonlyState)); // true
    // console.log(isReadonly(shallowReadonlyState.name)); // false
    // console.log(isReadonly(shallowReadonlyState.info)); // false

    // shallowReadonlyState.name = '李四'; // warning!!!
    // shallowReadonlyState.info.age = 23;
    // console.log(shallowReadonlyState.info.age); // 23

    // return {
    //   ...toRefs(shallowState),
    //   onChange
    // }

    // --------------------------------------------------

    // toRaw
    // 返回 reactive 或 readonly 代理的原始对象
    // 可用于临时读取数据而无需承担代理访问/跟踪的开销，也可用于写入数据而避免触发更改
    // 不建议保留对原始对象的持久引用。请谨慎使用。

    const obj = {
      a: 1,
      b: 2,
      more: {
        c: 3,
        d: 4
      }
    };

    // const proxyObj = reactive(obj);
    // console.log(proxyObj);
    // const newObj = toRaw(proxyObj);
    // console.log(newObj);
    // console.log(newObj === obj); // true

    // --------------------------------------------------

    // markRaw
    // 标记一个对象，使其永远不会转换为 proxy。返回对象本身。

    const rawObj = markRaw(obj);

    // console.log(rawObj); // 添加了 __v_skip: true 属性
    // console.log(rawObj === obj);

    // const proxyObj = reactive(rawObj);

    // console.log(isReactive(proxyObj)); // false

    // 嵌套在其他响应式对象中时也可以使用
    // const newProxyObj = reactive({ proxyObj })

    // console.log(newProxyObj);
    // console.log(isReactive(newProxyObj)); // true
    // console.log(isReactive(newProxyObj.proxyObj)); // false


    const proxyObj = reactive({
      nested: rawObj.more
    })
    console.log(proxyObj.nested);
    console.log(isReactive(proxyObj.nested)); // true
    console.log(obj.more === proxyObj.nested); // false

    // markRaw 和 shallowXXX API 使可以有选择地退出默认的深度响应式/只读转换模式，并将原始的，未被代理的对象嵌入状态图中(浅层次的代理)

    // 有些值不应该是响应式的，例如复杂的第三方类实例或 Vue 组件对象。
    // 当渲染具有不可变数据源的大列表时，跳过 proxy 转换可以提高性能。

    // 原始选择退出仅在根级别.
    // 因此，如果将嵌套在内的、未标记的原始对象添加进响应式对象，然后再次访问该响应式对象，就会得到原始对象被代理后的版本。

    // 这可能会导致同一性风险——
    //     即执行一个依赖于对象本身的操作，但同时使用同一对象的原始版本和被代理后的版本

  }
}
</script>

<style>

</style>
